/*
 * Modules related API
 */

function fnModuleCommonRowCallback(aData, nRow) {
	/* Human readble size */
	$("td:eq(23)", nRow).text( humanFileSize(aData[25]) );
}

function fnModuleTableCommonProc(moduleTable, selectProc) {
	$("#modules tbody").on("click", "tr", function() {
		if ($(this).hasClass('selected')) {
			$(this).removeClass('selected');
		} else {
			moduleTable.$('tr.selected').removeClass('selected');
			$(this).addClass('selected');
		}

		if (selectProc) {
			selectProc(moduleTable.fnGetData(this));
		}
	});
}

function fnGetModulesRestUrl(id, cmd, args) {
	prefix = fnGetProductRestPrefix();
	url =  prefix + "modules/" + id + "/" + cmd;

	if (args) {
		url = url + "?" + fnArgsToURI(args);
	}
	return url;
}

function fnGetDepsRestUrl(id, cmd, args) {
	prefix = fnGetProductRestPrefix();
	url =  prefix + "deps/" + id + "/" + cmd;

	if (args) {
		url = url + "?" + fnArgsToURI(args);
	}
	return url;
}

function fnEvtShowCallTables(id, type) {
	createDialogWithTable("symbol_dialog");

	var url = fnGetModulesRestUrl(id, "fields", {"type":type});
	var symbolTable = $('#symbol_table').dataTable( {
		"bProcessing": true,
		"sAjaxSource": url,
		"bDestroy": true,
		"sScrollY": "300px",
		"sAjaxDataProp": "data"
	} );
	$('#download_xml_symbols').unbind ('click').click(id, function (evt) {
		window.open(fnGetModulesRestUrl(evt.data, "fields", {"type":type, "format":"xml"}));
	});
	$('#symbol_dialog').appendTo("body").modal("show");
}

function fnEvtShowSymbolsForDeps(id) {
	createDialogWithTable("callinfo_dialog");

	var url = fnGetDepsRestUrl(id, "symbols");
	var symbolTable = $('#callinfo_table').dataTable( {
		"bProcessing": true,
		"sAjaxSource": url,
		"bDestroy": true,
		"sAjaxDataProp": "data"
	} );
	$('#download_xml_callinfo').unbind ('click').click(id, function (evt) {
		window.open(fnGetDepsRestUrl(evt.data, "symbols", {"format":"xml"}));
	});
	$('#callinfo_dialog').appendTo("body").modal("show");
}

function fnEvtShowDepsTables(id, type) {
	createDialogWithTable("deps_dialog");

	url = fnGetModulesRestUrl(id, "fields", {"type": type});
	var symbolTable = $('#depends').dataTable( {
		"bProcessing": true,
		"pageLength": 15,
		"bPaginate": true,
		"sAjaxSource": url,
		"bDestroy": true,
		"sAjaxDataProp": "data",
		"columnDefs": [{
			visible: false,
			targets: [0,1,2],
		}],
		"fnRowCallback": function( nRow, aData, iDisplayIndex ) {
			/* Append the grade to the default row class name */
			dataToLink = aData[5];
			if (dataToLink > 0) {
				$('td:eq(2)', nRow).html( '<a href="javascript:fnEvtShowSymbolsForDeps(' + aData[0] + ')">' + dataToLink + '</a>' );
			}

			var link = '<a href="javascript:fnShowModuleDetails(\'' + aData[1] + '\')">' + aData[3] + '</a>';
			$('td:eq(0)', nRow).html(link);

			var link = '<a href="javascript:fnShowModuleDetails(\'' + aData[2] + '\')">' + aData[4] + '</a>';
			$('td:eq(1)', nRow).html(link);
			return nRow;
		}
	} );
	$('#download_xml_deps').unbind ('click').click(id, function (evt) {
		window.open(fnGetModulesRestUrl(evt.data, "fields", {"type":type, "format":"xml", "details":1}));
	});
	$('#download_svg_deps').unbind ('click').click(id, function (evt) {
		window.open(fnGetModulesRestUrl(evt.data, "fields", {"type":type, "format":"svg"}));
	});
	$('#download_png_deps').unbind ('click').click(id, function (evt) {
		window.open(fnGetModulesRestUrl(evt.data, "fields", {"type":type, "format":"png"}));
	});
	$('#download_dot_deps').unbind ('click').click(id, function (evt) {
		window.open(fnGetModulesRestUrl(evt.data, "fields", {"type":type, "format":"dot"}));
	});
	$('#deps_dialog').appendTo("body").modal("show");

	setTimeout(function() {
		$("#depends").attr("style", "width:100%");
	}, 10);
}

function fnShowHelpInfo(type) {
	createDialogWithTable("help_info_dialog");
	$("#help_info_img").attr("src", "/images/" + type);
	$('#help_info_dialog').appendTo("body").modal("show");
}

function fnEvtFuncCallGraph (rowData, category) {
	var module_id = rowData[0];

	xargs = {"type":"deps", "format":"svg"};
	if (category) {
		xargs["__category"] = category;
	}
	$("#svg_callgraph").attr("data", fnGetModulesRestUrl(module_id, "fields", xargs));
}

var total_colums = 42;
var hidden_cols = 2;

var select_types = ["content", "range", "list", "dict"];
var filterDict = {
	"module_footer": {"idx": hidden_cols, "type": "edit"},
	"deps_footer": {"idx": hidden_cols + 1, "type": "range", "range": [{"val":0}, {"min":0}, {"min":0, "max":10}, {"min":10}]},
	"dependedBy_footer": {"idx": hidden_cols + 2, "type": "range", "range": [{"val":0}, {"min":0}, {"min":0, "max":10}, {"min":10}]},
};

function matchWithRange(val, range) {
	//console.log("match " + val + " with range: " + getRangeName(range));
	if ("val" in range) {
		if (val == range["val"]) {
			return true;
		}
		return false;
	} else if ("min" in range) {
		if ("max" in range) {
			if ((val > range["min"]) && (val < range["max"])) {
				return true;
			}
			return false;
		}
		if (val > range["min"]) {
			return true;
		}
		return false;
	} else if ("max" in range) {
		if (val < range["max"]) {
			return true;
		}
		return false;
	}
	return true;
}

function dataMatchWithFilterDict(data, ele, filter) {
	if (select_types.includes(filter["type"])) {
		ele = ele + " select";
	} else {
		ele = ele + " input";
	}
	var filterVal = $(ele).val();
	if (filterVal === undefined || (filterVal === "")) {
		//console.log(ele + ": [" + filterVal + "] no need filter");
		return true;
	}
	if (filter["type"] == "range") {
		if (filterVal == 0 || filterVal === "0") {
			return true;
		}
		return matchWithRange(data[filter["idx"]], filter["range"][filterVal - 1]);
	}
	if (select_types.includes(filter["type"])) {
		if (data[filter["idx"]] === filterVal) {
			return true;
		}
		return false;
	}
	if (!data[filter["idx"]].includes(filterVal)) {
		//console.log("filter val [" + filterVal + "] not matched.");
		return false;
	}
	return true;
}

function getRangeName(range) {
	if ("val" in range) {
		return range["val"];
	} else if ("min" in range) {
		if ("max" in range) {
			return range["min"] + "-" + range["max"];
		}
		return ">" + range["min"];
	} else if ("max" in range) {
		return "<" + range["max"];
	}
	return "";
}

function fnCreateRangeSelect(filter) {
	var r='<select><option value=></option>';
	filter["range"].forEach(function (val, idx, arr) {
		idx = idx + 1;
		r += '<option value=' + idx + '>' + getRangeName(val) + '</option>';
	});
	return r+'</select>';
}

function fnCreateListSelect(filter) {
	var r='<select><option value=></option>';
	filter["list"].forEach(function (val, idx, arr) {
		r += '<option value=' + val + '>' + val + '</option>';
	});
	return r+'</select>';
}

function fnCreateDictSelect(filter) {
	var r='<select><option value=></option>';
	for (val in filter["dict"]) {
		r += '<option value="' + val + '">' + filter["dict"][val] + '</option>';
	};
	return r+'</select>';
}

function createStaticFilterFooters(moduleTable) {
	$.fn.dataTable.ext.search.push(function (settings, data, dataIndex) {
		if (settings.sTableId != "modules") {
			return true;
		}
		for (var ele_id in filterDict) {
			var ele = '#' + ele_id;
			filter = filterDict[ele_id];
			if (!dataMatchWithFilterDict(data, ele, filter)) {
				return false;
			}
		}
		return true;
	});

	for (var ele_id in filterDict) {
		var ele = '#' + ele_id;
		filter = filterDict[ele_id];
		if (filter["type"] == "edit") {
			$(ele).html('<input type="text">');
			$("input", ele).keyup( function () {
				moduleTable.fnDraw();
			} );
			continue;
		}
		if (filter["type"] == "range") {
			$(ele).html(fnCreateRangeSelect(filter));
			$("select", ele).change( function () {
				moduleTable.fnDraw();
			} );
			continue;
		}
		if (filter["type"] == "list") {
			$(ele).html(fnCreateListSelect(filter));
			$("select", ele).change( function () {
				moduleTable.fnDraw();
			} );
			continue;
		}
		if (filter["type"] == "dict") {
			$(ele).html(fnCreateDictSelect(filter));
			$("select", ele).change( function () {
				moduleTable.fnDraw();
			} );
			continue;
		}
	};
}

function createDynamicFilterFooters(moduleTable) {
	window.setTimeout( function () {
		for (var ele_id in filterDict) {
			var ele = '#' + ele_id;
			filter = filterDict[ele_id];
			if (filter["type"] != "content") {
				continue;
			}
			$(ele).html(fnCreateSelect( moduleTable.fnGetColumnData(filter["idx"]) ));
			$('select', $(ele)).change( function () {
				moduleTable.fnDraw();
			} );
		}
	}, 1500);
}

var details_dict = {
	"basic_info": {
		"name": "基础信息",
		"rows": [{
			"key": "category",
			"name": "类型"
		}, {
			"key": "path",
			"name": "模块路径"
		}]
	},
	"resource_info": {
		"name": "ROM&RAM消耗",
		"rows": [{
			"key": "size",
			"name": ["ROM", "文件大小"]
		}, {
			"key": "text_size",
			"name": ["ROM", "text段大小"]
		}, {
			"key": "data_size",
			"name": ["ROM", "data段大小"]
		}, {
			"key": "oc_size",
			"name": ["ROM", "Object C段大小"]
		}, {
			"key": "processes",
			"name": ["RAM", "被加载的进程数"]
		}, {
			"key": "Pss",
			"name": ["RAM", "Pss内存信息"]
		}, {
			"key": "swapPss",
			"name": ["RAM", "Pss内存信息"]
		}, {
			"key": "Shared_Dirty",
			"name": ["RAM", "Shared_Dirty内存信息"]
		}, {
			"key": "Private_Dirty",
			"name": ["RAM", "Pss内存信息"]
		}]
	},
	"deps_info": {
		"name": "依赖信息",
		"rows": [{
			"name": ['<a href="javascript:fnShowHelpInfo(\'deps.png\')">详细定义</a>', "英文", "含义", "数值"]
		}, {
			"key": "deps",
			"name": ["D(n)", "deps", "直接依赖模块个数"]
		}, {
			"key": "deps_indirect",
			"name": ["D_I(n)", "deps_indirect", "间接依赖模块个数"]
		}, {
			"key": "deps_total",
			"name": ["D_T(n)", "deps_total", "依赖模块总个数"]
		}, {
			"key": "depth",
			"name": ["D_D", "deps_depth", "依赖深度"]
		}]
	},
	"dependedBy_info": {
		"name": "被依赖信息",
		"rows": [{
			"name": ['<a href="javascript:fnShowHelpInfo(\'dependedBy.png\')">详细定义</a>', "英文", "含义", "数值"]
		}, {
			"key": "dependedBy",
			"name": ["B(n)", "dependedBy", "被直接依赖模块个数"]
		}, {
			"key": "dependedBy_indirect",
			"name": ["B_I(n)", "dependedBy_indirect", "被间接依赖模块个数"]
		}, {
			"key": "dependedBy_total",
			"name": ["B_T(n)", "dependedBy_total", "被依赖模块总个数"]
		}, {
			"key": "dependedBy_depth",
			"name": ["B_D", "dependedBy_depth", "被依赖深度"]
		},]
	},
	"symbols_info": {
		"name": "符号信息",
		"rows": [{
			"name": ['<a href="javascript:fnShowHelpInfo(\'symbols.png\')">详细定义</a>', "英文", "含义", "数值"]
		}, {
			"key": "provided",
			"name": ["S_P(n)", "provided", "提供的符号个数"]
		}, {
			"key": "public_symbols",
			"name": ["S_P(n)", "public_symbols", "公共符号个数"]
		}, {
			"key": "used",
			"name": ["S_Pt(n)", "truely provided", "被使用到的符号个数"]
		}, {
			"key": "unused",
			"name": ["S_Pf(n)", "falsely provided", "未被使用到的符号个数"]
		}, {
			"key": "needed",
			"name": ["S_U(n)", "undefined", "未定义符号个数"]
		}, {
			"key": "matched",
			"name": ["S_Ut(n)", "matched undefined", "与依赖库匹配的未定义符号个数"]
		}, {
			"key": "unmatched",
			"name": ["S_Pf(n)", "unmatched undefined", "与依赖库无法匹配的未定义符号个数"]
		}, {
			"key": "duplicated",
			"name": ["S_Ud(n)", "duplicates", "与依赖库匹配的重复未定义符号个数"]
		}]
	}
};

function __generateDetailValue(category, key, moduleId, moduleData) {
	if (key === "dependedBy_depth") { // No link
		return moduleData[key];
	}
	if (key === "processes") {
		if (moduleData[key] > 0) {
			return '<a href="javascript:fnEvtShowProcessTables(' + moduleId + ')">' + humanFileSize(moduleData[key]) + '</a>';
		}
	} else if ((key === "depth") && (moduleData[key] > 0)) {
		return '<a href="' + fnGetModulesRestUrl(moduleId, "fields", {"type":key, "format":"png"}) +'" target="_blank">' + moduleData[key] + '<a/>';
	} else if ((category === "deps_info") || (category === "dependedBy_info")) {
		if (moduleData[key] > 0) {
			return  '<a href="javascript:fnEvtShowDepsTables(' + moduleId + ', \'' + key + '\')">' + moduleData[key] + '</a>';
		}
	} else if (category === "symbols_info") {
		if (moduleData[key] > 0) {
			return  '<a href="javascript:fnEvtShowCallTables(' + moduleId + ', \'' + key + '\')">' + moduleData[key] + '</a>';
		}
	} else if (category === "resource_info") {
		return humanFileSize(moduleData[key]);
	} else if (category === "basic_info") {
		if (key === "sa_id") {
			return moduleData[key];
		}
		if (key === "chipset") {
			if (moduleData[key] === "0") {
				return "系统组件";
			}
			return "芯片组件";
		}
		if (moduleData[key] === "0") {
			return "否";
		} else if (moduleData[key] === "1") {
			return "是";
		}
	}
	return moduleData[key];
}

function _generateCollapse(parent, name, info, show, moduleId, moduleData) {
	var header = '<div class="card">';

	// Header div
	header += '<div class="card-header" id="heading' + name + '">';
	header += '<h5 class="mb-0">';
	header += '<button class="btn btn-link" id="collapse_btn_' + name + '">';
	header += info["name"] + '</button>';
	header += '</h5>';
	header += '</div>';

	// collapse content div
	header += '<div id="collapse_' + name + '" class="collapse ' + show + '" aria-labelledby="heading' + name + '" data-parent="#' + parent + '">';
	header += '<div class="container">';

	info["rows"].forEach(function (val, idx, arr) {
		header += '<div class="row border-bottom">';
		if (Array.isArray(val["name"])) {
			val["name"].forEach(function (subName, subIdx, subArr) {
				header += '<div class="col border-end">' + subName + '</div>';
			});
		} else {
			header += '<div class="col border-end">' + val["name"] + '</div>';
		}
		if ("key" in val) {
			header += '<div class="col text-end">' + __generateDetailValue(name, val["key"], moduleId, moduleData) + '</div>';
		}
		header += '</div>';
	});

	header += '</div>';
	header += '</div>';

	header += '</div>';
	return header;
}

function _addCollapseClickFunc() {
	for (var collaps in details_dict) {
		$("#collapse_btn_"+collaps).unbind ('click').click(collaps, function (evt) {
			$("#collapse_"+evt.data).toggle();
		});
	}
}

function fnShowModuleDetails(moduleId) {
	$.get(fnGetModulesRestUrl(moduleId, "details", {"format":"json"}), function (data, status) {
		createDialogWithTable("module_dialog");
		var detailHtml = "";
		var show = "show";
		for (var collaps in details_dict) {
			detailHtml += _generateCollapse("accordion", collaps, details_dict[collaps], show, moduleId, JSON.parse(data));
			show = "hide";
		}
		$("#accordion").html(detailHtml);
		$('#module_dialog').appendTo("body").modal("show");
		_addCollapseClickFunc();
	});
}

var moduleTable;

function loadModules() {
	let symbol_table = new DataTable('#symbol_table', {
	});

	$('#all_modules').click(function () {
		window.open(fnGetAllItemsFilterUrl("modules", {"format":"xml"}));
	});

	// Check if DataTable is already initialized
	if ($.fn.DataTable.isDataTable('#modules')) {
		// If already initialized, just destroy and recreate
		$('#modules').DataTable().destroy();
	}

	moduleTable = $('#modules').dataTable( {
		"bProcessing": true,
		"sAjaxSource": fnGetAllItemsFilterUrl("modules"),
		"sScrollX": "1000px",
		//"sScrollY": "400px",
		"bPaginate": true,
		"bDestroy": true,
		"sAjaxDataProp": "data",
		"fnServerData": function ( sSource, aoData, fnCallback ) {
			$.ajax( {
				"dataType": 'json',
				"type": "GET",
				"url": sSource,
				"data": aoData,
				"success": fnCallback,
				"error": function (xhr, textStatus, errorThrown) {
					// Handle the error gracefully
					console.error('Modules API Error:', textStatus, errorThrown);
					console.error('Response:', xhr.responseText);

					// Show error message instead of DataTables warning
					$('#modules').DataTable().destroy();
					$('#modules').html('<div style="padding: 20px; text-align: center; color: #666;"><h3>模块数据暂时不可用</h3><p>错误信息：' + (xhr.responseJSON ? xhr.responseJSON.detail : errorThrown) + '</p><p>请联系管理员检查数据库配置。</p></div>');
				}
			} );
		},
		"columnDefs": [{
			visible: false,
			targets: [0,1],
		}],
		"fnRowCallback": function( nRow, aData, iDisplayIndex ) {
			fnModuleCommonRowCallback(aData, nRow);

			pos = 2;
			var link = '<a href="javascript:fnShowModuleDetails(' + aData[0] + ')">' + aData[pos] + '</a>';
			var ele = 'td:eq(' + (pos-2).toString() + ')';
			$(ele, nRow).html(link);

			var startIdx = 3;
			["deps", "dependedBy"].forEach(function (val, idx, arr) {
				pos = startIdx+idx;
				if (aData[pos] > 0) {
					var link = '<a href="javascript:fnEvtShowDepsTables(' + aData[0] + ", '" + val + "')\">" + aData[pos] + '</a>';
					var ele = 'td:eq(' + (pos-2).toString() + ')';
					$(ele, nRow).html(link);
				}
			});

			startIdx = 5;
			["provided", "public_symbols", "used", "unused", "needed", "matched", "duplicated", "unmatched"].forEach(function (val, idx, arr) {
				pos = startIdx+idx;
				if (aData[pos] > 0) {
					var link = '<a href="javascript:fnEvtShowCallTables(' + aData[0] + ", '" + val + "')\">" + aData[pos] + '</a>';
					var ele = 'td:eq(' + (pos-2).toString() + ')';
					$(ele, nRow).html(link);
				}
			});

			pos = 18;
			if (aData[pos] > 0) {
				var link = '<a href="javascript:fnEvtShowProcessTables(' + aData[1] + ")\">" + aData[pos] + '</a>';
				var ele = 'td:eq(' + (pos-2).toString() + ')';
				$(ele, nRow).html(link);
			}

			/* Human readble size */
			//$('td:eq(10)', nRow).text( humanFileSize(aData[26]) );
			//$('td:eq(11)', nRow).text( humanFileSize(aData[27]) );

			return nRow;
		}
	} );

	fnModuleTableCommonProc(moduleTable, function (selectedData) {
		fnEvtFuncCallGraph(selectedData);
	});
	createDynamicFilterFooters(moduleTable);
	createStaticFilterFooters(moduleTable);
}
